package client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URL;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import view.MainWindow;

public class Client implements ClientInterface {

    private final int BUFFERSIZE = 1; //Bytes of buffer to download or upload.

    private MainWindow mainWindow;
    private Socket socket;

    private String hostName;
    private int portNumber;

    private URL remoteFolder;
    private URL localFolder;

    private BufferedReader inputServer;
    private PrintWriter outputServer;

    public Client(String hostName, int portNumber, MainWindow mainWindow) {

        this.mainWindow = mainWindow;
        this.hostName = hostName;
        this.portNumber = portNumber;

    }

    @Override
    public synchronized void connect() throws IOException {

        this.socket = new Socket(hostName, portNumber);
        this.inputServer = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
        this.outputServer = new PrintWriter(this.socket.getOutputStream(), true);

    }

    @Override
    public synchronized void listRemoteFiles() {
        try {
            outputServer.println("LIST");
            int port = Integer.parseInt(inputServer.readLine());
            Socket lSocket = new Socket(hostName, port);

            InputStream inS = lSocket.getInputStream();
            ObjectInputStream mapInputStream = new ObjectInputStream(inS);
            Map<File, Map> mapFiles = (Map<File, Map>) mapInputStream.readObject();

            mapInputStream.close();
            inS.close();
            lSocket.close();

            this.mainWindow.setTree(mapFiles);

        } catch (IOException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    @Override
    public void downloadFile(MainWindow mainWindow, String stringRelativePath, File localFile, int currentBar) {
        try {
            System.out.println("Downloading");
            outputServer.println("GET " + "\"" + stringRelativePath + "\"");
            int port = Integer.parseInt(inputServer.readLine());
            int size = Integer.parseInt(inputServer.readLine());
            new Thread(new DownloadFileThread(mainWindow, stringRelativePath, localFile, currentBar, port, size)).start();
        } catch (IOException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private class DownloadFileThread implements Runnable {

        private MainWindow mainWindow;
        private String stringRelativePath;
        private File localFile;
        private int currentBar;

        private int port;
        private int size;

        public DownloadFileThread(MainWindow mainWindow, String stringRelativePath, File localFile, int currentBar, int port, int size) {

            this.mainWindow = mainWindow;
            this.stringRelativePath = stringRelativePath;
            this.localFile = localFile;
            this.currentBar = currentBar;
            this.port = port;
            this.size = size;
        }

        @Override
        public void run() {
            try {
                Socket dSocket = new Socket(hostName, this.port);
                this.mainWindow.addTransferBar();
                this.mainWindow.setTransferBarInfo(this.currentBar, "Download: " + this.stringRelativePath, 0);
                int count;
                byte[] buffer = new byte[BUFFERSIZE];
                localFile.createNewFile();
                InputStream is = dSocket.getInputStream();
                FileOutputStream fos = new FileOutputStream(localFile);
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                int done = 0;
                while ((count = is.read(buffer)) > 0) {
                    bos.write(buffer, 0, count);
                    bos.flush();
                    done += count;
                    this.mainWindow.setTransferBarInfo(this.currentBar, "Download: " + this.stringRelativePath, (done) * 100 / size);

                }

                bos.close();
                dSocket.close();
                System.out.println("Download done. "+this.stringRelativePath);

                this.mainWindow.setTransferBarInfo(this.currentBar, "Download: " + this.stringRelativePath, 100);

            } catch (IOException ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }
}
